import * as THREE from "three";
import { Canvas, useFrame } from "@react-three/fiber";
import { useRef, useMemo, useState, useEffect } from "react";
import { ThreeEvent } from "@react-three/fiber";

const NUM_PARTICLES = 600; // 粒子数量
const ATTRACT_RADIUS = 3.5; // 吸引范围扩大点
const PARTICLE_SIZE = 0.25; // 粒子大小调大

const Particles: React.FC = () => {
  const particlesRef = useRef<THREE.Points>(null);
  const mouse = useRef<THREE.Vector2>(new THREE.Vector2(-1, -1));
  const [hovered, setHovered] = useState(false);

  const positions = useMemo(() => {
    const temp: number[] = [];
    for (let i = 0; i < NUM_PARTICLES; i++) {
      temp.push((Math.random() - 0.5) * 30);
      temp.push((Math.random() - 0.5) * 30);
      temp.push((Math.random() - 0.5) * 30);
    }
    return new Float32Array(temp);
  }, []);

  const velocities = useMemo<THREE.Vector3[]>(() => {
    const temp: THREE.Vector3[] = [];
    for (let i = 0; i < NUM_PARTICLES; i++) {
      temp.push(new THREE.Vector3(
        (Math.random() - 0.5) * 0.001,
        (Math.random() - 0.5) * 0.001,
        (Math.random() - 0.5) * 0.001
      ));
    }
    return temp;
  }, []);

  const opacities = useRef(new Float32Array(NUM_PARTICLES).fill(0.5));

  useEffect(() => {
    document.body.style.cursor = hovered ? "none" : "default";
    return () => {
      document.body.style.cursor = "default";
    };
  }, [hovered]);

  useFrame(({ clock }) => {
    if (!particlesRef.current || !particlesRef.current.geometry) return;

    const positionsArray = particlesRef.current.geometry.attributes.position.array;
    const opacitiesArray = opacities.current;
    const time = clock.getElapsedTime();

    const mouse3D = new THREE.Vector3(mouse.current.x * 15, mouse.current.y * 15, 0);

    for (let i = 0; i < NUM_PARTICLES; i++) {
      const ix = i * 3;
      const particlePos = new THREE.Vector3(
        positionsArray[ix],
        positionsArray[ix + 1],
        positionsArray[ix + 2]
      );

      const dist = particlePos.distanceTo(mouse3D);

      if (dist < ATTRACT_RADIUS) {
        // 有吸引力，但不是直接移动到鼠标
        const attraction = mouse3D.clone().sub(particlePos)
          .normalize()
          .multiplyScalar(0.03 * (1 - dist / ATTRACT_RADIUS)); // 距离越近吸力越大
        velocities[i].add(attraction);

        // 透明度升高
        opacitiesArray[i] = THREE.MathUtils.lerp(opacitiesArray[i], 1.0, 0.2);
      } else {
        opacitiesArray[i] = THREE.MathUtils.lerp(opacitiesArray[i], 0.5, 0.02);
      }

      // 加上缓慢噪声
      const noise = Math.sin(time + i) * 0.0015;
      particlePos.add(new THREE.Vector3(noise, noise, noise));

      // 更新粒子位置
      particlePos.add(velocities[i]);
      velocities[i].multiplyScalar(0.98);

      // 出界重置
      if (particlePos.length() > 20) {
        particlePos.set(
          (Math.random() - 0.5) * 30,
          (Math.random() - 0.5) * 30,
          (Math.random() - 0.5) * 30
        );
      }

      positionsArray[ix] = particlePos.x;
      positionsArray[ix + 1] = particlePos.y;
      positionsArray[ix + 2] = particlePos.z;
    }

    particlesRef.current.geometry.attributes.position.needsUpdate = true;
    particlesRef.current.geometry.attributes.opacity.needsUpdate = true;
  });

  const handlePointerMove = (e: ThreeEvent<PointerEvent>) => {
    mouse.current.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.current.y = -(e.clientY / window.innerHeight) * 2 + 1;
  };

  const handlePointerOver = () => setHovered(true);
  const handlePointerOut = () => setHovered(false);

  return (
    <points
      onPointerMove={handlePointerMove}
      onPointerOver={handlePointerOver}
      onPointerOut={handlePointerOut}
      ref={particlesRef}
    >
      <bufferGeometry>
        <bufferAttribute
          attach="attributes-position"
          array={positions}
          count={positions.length / 3}
          itemSize={3}
        />
        <bufferAttribute
          attach="attributes-opacity"
          array={opacities.current}
          count={opacities.current.length}
          itemSize={1}
        />
      </bufferGeometry>
      <pointsMaterial
        size={PARTICLE_SIZE}
        transparent
        depthWrite={false}
        blending={THREE.AdditiveBlending}
        onBeforeCompile={(shader) => {
          shader.vertexShader = shader.vertexShader.replace(
            `void main() {`,
            `attribute float opacity; varying float vOpacity; void main() { vOpacity = opacity;`
          );
          shader.fragmentShader = shader.fragmentShader.replace(
            `void main() {`,
            `varying float vOpacity; void main() {`
          ).replace(
            `gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,
            `gl_FragColor = vec4( outgoingLight, vOpacity );`
          );
        }}
      />
    </points>
  );
};

const ParticlesBackground: React.FC = () => {
  return (
    <>
      <Canvas
        camera={{ position: [0, 0, 20], fov: 65 }}
        style={{
          position: "fixed",
          top: 0,
          left: 0,
          width: "100vw",
          height: "100vh",
          background: "#ffffff",
          zIndex: 0,
        }}
      >
        <Particles />
      </Canvas>
      {/*  <CustomCursor />*/}
    </>
  );
};

export default ParticlesBackground;
